#include "riolib.h"

/*********************************************************************
 * The Rio package - a small set of robust, mutually compatible, and 
 * reentrant I/O functions. Subsumes Steven's readn, writen, and 
 * readline functions. 
 **********************************************************************/

/* 
 * rio_read - This is a wrapper for the Unix read() function that
 *    transfers min(n, rio_cnt) bytes from an internal buffer to a user
 *    buffer, where n is the number of bytes requested by the user and
 *    rio_cnt is the number of unread bytes in the internal buffer. On
 *    entry, rio_read() refills the internal buffer via a call to
 *    read() if the internal buffer is empty.
 */
/* $begin rio_read */
static ssize_t rio_read(rio_t *rp, char *usrbuf, size_t n)
{
    int cnt;

    /* If the buffer is empty, then refill it */
    while (rp->rio_cnt <= 0) { 
	rp->rio_cnt = read(rp->rio_fd, rp->rio_buf,  sizeof(rp->rio_buf));
	if (rp->rio_cnt < 0) {
	    if (errno != EINTR) /* restart if interrupted */
		return -1;
	}
	else if (rp->rio_cnt == 0)  /* EOF */
	    return 0;
	else 
	    rp->rio_bufptr = rp->rio_buf; /* reset buffer ptr */
    }

    /* Copy min(n, rp->rio_cnt) bytes from internal buf to user buf */
    cnt = n;
    if (rp->rio_cnt < n)
	cnt = rp->rio_cnt;
    memcpy(usrbuf, rp->rio_bufptr, cnt);
    rp->rio_bufptr += cnt;
    rp->rio_cnt -= cnt;
    return cnt;
}
/* $end rio_read */

/*
 * rio_init - Rio init function called once per open descriptor, before
 *    any calls to rio_readn or rio_readling.
 */
/* $begin rio_init */
int rio_init(rio_t *rp, int fd) 
{
    if (fd < 0)
	return -1;
    rp->rio_fd = fd;  
    rp->rio_cnt = 0;  
    rp->rio_bufptr = rp->rio_buf;
    return 0;
}
/* $end rio_init */

/*
 * rio_readn - Robustly read n bytes
 */
/* $begin rio_readn */
ssize_t rio_readn(rio_t *rp, void *usrbuf, size_t n) 
{
    size_t nleft = n;
    ssize_t nread;
    char *bufp = usrbuf;
    
    while (nleft > 0) {
	if ((nread = rio_read(rp, bufp, nleft)) < 0) {
	    if (errno == EINTR)
		nread = 0;      /* call read() again */
	    else
		return -1;      /* errno set by read() */ 
	} 
	else if (nread == 0)
	    break;              /* EOF */
	nleft -= nread;
	bufp += nread;
    }
    return (n - nleft);    /* return >= 0 */
}
/* $end rio_readn */

/* 
 * rio_readline - robustly read a text line 
 */
/* $begin rio_readline */
ssize_t rio_readline(rio_t *rp, void *usrbuf, size_t maxlen) 
{
    int n, rc;
    char c;
    char *bufp = usrbuf;

    for (n = 1; n < maxlen; n++) { 
	if ((rc = rio_read(rp, &c, 1)) == 1) {
	    *bufp++ = c;
	    if (c == '\n')
		break;
	} else if (rc == 0) {
	    if (n == 1)
		return 0; /* EOF, no data read */
	    else
		break;    /* EOF, some data was read */
	} else
	    return -1;	  /* error */
    }
    *bufp = 0;
    return n;
}
/* $end rio_readline */

/*
 * rio_writen - robustly write n bytes
 */
/* $begin rio_writen */
ssize_t rio_writen(int fd, void *usrbuf, size_t count) 
{
    size_t nleft = count;
    ssize_t nwritten;
    char *bufp = usrbuf;

    while (nleft > 0) {
	if ((nwritten = write(fd, bufp, nleft)) <= 0) {
	    if (errno == EINTR)
		nwritten = 0;    /* and call write() again */
	    else
		return -1;       /* errorno set by write() */
	}
	nleft -= nwritten;
	bufp += nwritten;
    }
    return count;
}
/* $end rio_writen */
